﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using UnityEngine;
using Stopwatch = System.Diagnostics.Stopwatch;
//using UnityAsync;

namespace Jinndev.Async {

    /// <summary>
    /// 加载处理器
    /// </summary>
    public class TaskHandler : MonoBehaviour {

        public static TaskHandler Create(bool autoDestroy) {
            TaskHandler loadingHandler = new GameObject("TaskHandler").AddComponent<TaskHandler>();
            loadingHandler.autoDestroy = autoDestroy;
            return loadingHandler;
        }

        public event Action<int, int, string> OnStartAction; // 开始执行一个Action，参数<index, total, description>
        public event Action<int, int, string, double> OnEndAction; // 执行完成一个Action，参数<index, total, milliseconds>
        public event Action<int, int, float> OnProcessAction; // 当前Action的执行进度百分比，参数<index, total, float>
        public event Action OnComplete;
        public event Action<Exception> OnError;

        public float taskInterval; // 任务执行间隔时间，单位是秒
        public float subTaskInterval; // 子任务执行间隔时间，单位是秒
        public bool autoDestroy; // 完成后是否自动销毁
        public bool completed { get; private set; }

        private List<TaskQueue> taskQueueList = new List<TaskQueue>();
        private Exception exception = null;

        public void Add(TaskQueue taskQueue) {
            taskQueueList.Add(taskQueue);
        }

        /// <summary>
        /// 开始异步加载
        /// </summary>
        public void StartLoadingAsync() {
            //LoadingAsync();
            StartCoroutine(LoadingCoroutine());
        }

        ///// <summary>
        ///// 开始可等待的异步加载
        ///// </summary>
        ///// <returns></returns>
        //public async Task StartLoading() {
        //    await Loading();
        //    if (exception != null) {
        //        throw exception;
        //    }
        //}

        //private async void LoadingAsync() {
        //    await Loading();
        //}

        //private async Task Loading() {
        //    await new WaitForEndOfFrame();
        //    Stopwatch stopwatch = new Stopwatch();

        //    for (int i = 0; i < taskQueueList.Count; i++) {
        //        TaskQueue taskQueue = taskQueueList[i];
        //        if (OnStartAction != null) {
        //            OnStartAction.Invoke(i, taskQueueList.Count, taskQueue.description);
        //            await new WaitForEndOfFrame();
        //        }

        //        stopwatch.Reset();
        //        while (taskQueue.HasNext()) {
        //            stopwatch.Start();
        //            try {
        //                await taskQueue.InvokeNext();
        //            }
        //            catch (Exception e) {
        //                exception = e;
        //            }
        //            stopwatch.Stop();

        //            if (exception != null) {
        //                break; // break while
        //            }

        //            OnProcessAction?.Invoke(i, taskQueueList.Count, taskQueue.GetProgress());
        //            if (subTaskInterval > 0) {
        //                await new UnityAsync.WaitForSecondsRealtime(subTaskInterval);
        //            }
        //            else {
        //                await new WaitForEndOfFrame();
        //            }
        //        }
        //        if (exception != null) {
        //            break; // break for
        //        }

        //        if (OnEndAction != null) {
        //            OnEndAction.Invoke(i, taskQueueList.Count, taskQueue.description, stopwatch.Elapsed.TotalMilliseconds);
        //        }
        //        if (taskInterval > 0) {
        //            await new UnityAsync.WaitForSecondsRealtime(taskInterval);
        //        }
        //        else {
        //            await new WaitForEndOfFrame();
        //        }
        //    }

        //    stopwatch = null;
        //    if (exception == null) {
        //        completed = true;
        //        OnComplete?.Invoke();
        //    }
        //    else {
        //        //Debug.LogException(exception);
        //        OnError?.Invoke(exception);
        //    }

        //    if (autoDestroy) {
        //        Destroy(gameObject);
        //    }
        //}

        private IEnumerator LoadingCoroutine() {
            yield return new WaitForEndOfFrame();
            Stopwatch stopwatch = new Stopwatch();

            for (int i = 0; i < taskQueueList.Count; i++) {
                TaskQueue taskQueue = taskQueueList[i];
                if (OnStartAction != null) {
                    OnStartAction.Invoke(i, taskQueueList.Count, taskQueue.description);
                    yield return new WaitForEndOfFrame();
                }

                stopwatch.Reset();
                while (taskQueue.HasNext()) {
                    stopwatch.Start();
                    try {
                        taskQueue.InvokeNext();
                    }
                    catch (Exception e) {
                        exception = e;
                    }
                    stopwatch.Stop();

                    if (exception != null) {
                        break; // break while
                    }

                    OnProcessAction?.Invoke(i, taskQueueList.Count, taskQueue.GetProgress());
                    if (subTaskInterval > 0) {
                        yield return new UnityEngine.WaitForSecondsRealtime(subTaskInterval);
                    }
                    else {
                        yield return new WaitForEndOfFrame();
                    }
                }
                if (exception != null) {
                    break; // break for
                }

                if (OnEndAction != null) {
                    OnEndAction.Invoke(i, taskQueueList.Count, taskQueue.description, stopwatch.Elapsed.TotalMilliseconds);
                }
                if (taskInterval > 0) {
                    yield return new UnityEngine.WaitForSecondsRealtime(taskInterval);
                }
                else {
                    yield return new WaitForEndOfFrame();
                }
            }

            stopwatch = null;
            try {
                if (exception == null) {
                    completed = true;
                    OnComplete?.Invoke();
                }
                else {
                    //Debug.LogException(exception);
                    OnError?.Invoke(exception);
                }
            }
            catch (Exception e) {
                Debug.LogException(e);
            }

            if (autoDestroy) {
                Destroy(gameObject);
            }
        }

    }

}